﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Xml;
using repliback4.Net.Messages;
using repliback4.Net.Requests.Serialization;

namespace repliback4.XmlStreaming {

    public class XmlStreamException : Exception {
        public XmlStreamException ( String message ) : base( message ) { }
    }

    public class XmlStreamWriterSettings {
        /// <summary>
        /// 
        /// </summary>
        public Boolean AutoFlush = true;
        public Boolean NiceFormating = true;

        public static XmlStreamWriterSettings Default {
            get {
                XmlStreamWriterSettings defaultSettings = new XmlStreamWriterSettings( );

                return defaultSettings;
            }
        }
    }
    public class XmlStreamReaderSettings {
        public static XmlStreamReaderSettings Default {
            get {
                XmlStreamReaderSettings defaultSettings = new XmlStreamReaderSettings( );

                return defaultSettings;
            }
        }
    }


    public class XmlStreamWriter {
        #region Protected fields
        protected TextWriter masterWriter;

        protected XmlNamespaceManager nsManager;
        protected XmlDocument masterDocument;
        protected XmlWriter xmlWriter;
        protected XmlWriter elementWriter;
        protected StanzaSerializer stanzaSerializer;
        protected Boolean streamOpened = false;

        protected XmlStreamWriterSettings settings;

        #endregion
        #region Public Properies
        public XmlStreamWriterSettings Settings {
            get {
                if ( this.settings == null ) {
                    this.settings = XmlStreamWriterSettings.Default;
                }
                return this.settings;
            }
            set {
                if ( this.settings == null ) {
                    this.settings = value;
                } else {
                    throw new Exception( "cant change settings" );
                }
            }
        }

        public XmlNamespaceManager NamespaceManager {
            get {
                return this.nsManager;
            }
        }


        public Boolean StreamOpened {
            get {
                return streamOpened;
            }
        }
        #endregion
        #region Constructors

        public XmlStreamWriter ( TextWriter textWriter, XmlDocument document ) {
            this.masterDocument = document;
            this.masterWriter = textWriter;
            this.init( );
        }

        public XmlStreamWriter ( TextWriter textWriter, XmlDocument document, XmlStreamWriterSettings settings ) {
            this.masterDocument = document;
            this.masterWriter = textWriter;
            this.settings = settings;
            this.init( );
        }

        public XmlStreamWriter ( TextWriter textWriter, XmlDocument document, XmlStreamWriterSettings settings, XmlNamespaceManager namespaceManager ) {
            this.masterDocument = document;
            this.masterWriter = textWriter;
            this.settings = settings;
            this.nsManager = namespaceManager;
            this.init( );
        }



        protected void init ( ) {
            if ( this.nsManager == null ) {
                this.stanzaSerializer = new StanzaSerializer( this.masterDocument );
            } else {
                this.stanzaSerializer = new StanzaSerializer( this.masterDocument, this.nsManager );
            }
        }
        #endregion
        #region Open / close stream
        public void OpenStream ( ) {

            XmlWriterSettings xwrts = new XmlWriterSettings( );
            xwrts.ConformanceLevel = ConformanceLevel.Document;
            xwrts.CloseOutput = false;
            if ( this.settings.NiceFormating ) {
                xwrts.Indent = true;
                xwrts.NewLineOnAttributes = true;
            }

            xwrts.Encoding = Encoding.UTF8;

            this.xmlWriter = XmlTextWriter.Create( this.masterWriter, xwrts );

            this.xmlWriter.WriteStartDocument( );
            this.xmlWriter.Flush( );

            XmlWriterSettings ewrts = new XmlWriterSettings( );
            ewrts.ConformanceLevel = ConformanceLevel.Fragment;
            ewrts.CloseOutput = false;
            if ( this.settings.NiceFormating ) {
                ewrts.Indent = true;
                ewrts.NewLineOnAttributes = true;
            }
            ewrts.Encoding = Encoding.UTF8;

            this.elementWriter = XmlTextWriter.Create( this.masterWriter, ewrts );



            XmlElement rootElement = this.masterDocument.DocumentElement;


            this.elementWriter.WriteStartElement(
                    rootElement.Prefix,
                    rootElement.LocalName,
                    rootElement.NamespaceURI );

            foreach ( XmlAttribute attr in rootElement.Attributes ) {
                this.elementWriter.WriteAttributeString( attr.LocalName, attr.NamespaceURI, attr.Value );
            }
            //this.WriteElement( this.masterDocument.DocumentElement );
            if ( this.settings.AutoFlush ) this.elementWriter.Flush( );
            this.streamOpened = true;
        }

        public void CloseStream ( ) {
            this.elementWriter.WriteFullEndElement( );
            this.elementWriter.Flush( );
            this.streamOpened = false;
        }
        #endregion
        #region Write
        public void WriteElement ( XmlElement element ) {
            if ( !this.StreamOpened ) {
                throw new XmlStreamException( "Stream not opened, use first XmlStreamWriter.OpenStream()" );
            }
            element.WriteTo( this.elementWriter );
            if ( this.settings.AutoFlush ) this.Flush( );

        }

        public void WriteStanza ( Stanza stanza ) {
            if ( !this.StreamOpened ) {
                throw new XmlStreamException( "Stream not opened, use first XmlStreamWriter.OpenStream()" );
            }


            this.stanzaSerializer.serialize( stanza ).WriteTo( this.elementWriter );
            if ( this.settings.AutoFlush ) this.Flush( );

        }

        public void Flush ( ) {
            this.elementWriter.Flush( );
        }
        #endregion

    }

    /// <summary>
    /// 
    /// </summary>
    public class XmlStreamReader {
        #region Protected fields
        protected XmlDocument masterDocument;
        protected TextReader masterReader;
        protected XmlStreamReaderSettings settings;
        protected XmlNamespaceManager nsManager;
        #endregion
        #region Constructors
        /// <summary>
        /// 
        /// </summary>
        /// <param name="textReader">textreader</param>
        /// <param name="document">master document</param>
        public XmlStreamReader ( TextReader textReader, XmlDocument document ) {
            this.masterReader = textReader;
            this.masterDocument = document;
            this.settings = XmlStreamReaderSettings.Default;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="textReader"></param>
        /// <param name="document"></param>
        /// <param name="settings"></param>
        public XmlStreamReader ( TextReader textReader, XmlDocument document, XmlStreamReaderSettings settings ) {
            this.masterReader = textReader;
            this.masterDocument = document;
            this.settings = settings;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="textReader"></param>
        /// <param name="document"></param>
        /// <param name="settings"></param>
        /// <param name="namespaceManager"></param>
        public XmlStreamReader ( TextReader textReader, XmlDocument document, XmlStreamReaderSettings settings, XmlNamespaceManager namespaceManager ) {
            this.masterReader = textReader;
            this.masterDocument = document;
            this.settings = settings;
            this.nsManager = namespaceManager;
        }
        #endregion
        #region Open / Close Stream

        #endregion
    }



}
